# Copyright (C) 2023 Andrew Lutsenko <anlutsenko@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you may find one here:
# https://www.gnu.org/licenses/gpl-3.0.html
# or you may search the http://www.gnu.org website for the version 3 license,
# or you may write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA


from fastapi.testclient import TestClient
from unittest import TestCase
from unittest.mock import patch

from app.main import app, handler_task


@patch('app.main.GITLAB_SECRET', new='correct-secret')
class TestApp(TestCase):
    def test_invalid_secret(self):
        client = TestClient(app)
        response = client.post(
            "/issue", headers={"X-Gitlab-Token": "invalid-secret"}, json={})
        assert response.status_code == 403

    def test_invalid_json(self):
        client = TestClient(app)
        response = client.post(
            "/issue", headers={"X-Gitlab-Token": "correct-secret"}, content="not a json")
        assert response.status_code == 400

    @patch('app.main.handler_task')
    def test_ok(self, handler_task):
        client = TestClient(app)
        payload = {"a": "b"}
        response = client.post(
            "/issue",
            headers={
                "X-Gitlab-Token": "correct-secret",
                "X-Gitlab-Event": "Big Bang"
            },
            json=payload)
        assert response.status_code == 202
        assert response.content == b"OK"
        handler_task.assert_called_once_with("Big Bang", payload)


class TestHandlerTask(TestCase):
    def patch_class(self, name):
        patcher = patch(name)
        handler = patcher.start()
        self.addCleanup(patcher.stop)
        return handler

    def setUp(self):
        self.gitlab = self.patch_class("app.main.Gitlab")
        self.banned_user_handler = self.patch_class(
            "app.main.BannedUsersHandler")
        self.code_issue_handler = self.patch_class("app.main.CodeIssueHandler")
        self.banned_user_handler.return_value.handle.return_value = False
        self.code_issue_handler.return_value.handle.return_value = False

    def test_not_banned(self):
        payload = {"a": "b"}
        handler_task("event", payload)
        self.banned_user_handler.return_value.handle.assert_called_once_with(
            "event", payload)
        self.code_issue_handler.return_value.handle.assert_not_called()

    def test_banned(self):
        self.banned_user_handler.return_value.handle.return_value = True
        payload = {"a": "b"}
        handler_task("event", payload)
        self.banned_user_handler.return_value.handle.assert_called_once_with(
            "event", payload)
        self.code_issue_handler.return_value.handle.assert_not_called()

    @patch("app.main.KICAD_CODE_PROJECT_PATH", new="test-path")
    def test_code_issue(self):
        payload = {
            "a": "b",
            "project": {
                "path_with_namespace": "test-path"
            }
        }
        event = "Issue Hook"
        handler_task(event, payload)
        self.banned_user_handler.return_value.handle.assert_called_once_with(
            event, payload)
        self.code_issue_handler.return_value.handle.assert_called_once_with(
            event, payload)

    @patch("app.main.KICAD_CODE_PROJECT_PATH", new="test-path")
    def test_other_issue(self):
        payload = {
            "a": "b",
            "project": {
                "path_with_namespace": "other-path"
            }
        }
        event = "Issue Hook"
        handler_task(event, payload)
        self.banned_user_handler.return_value.handle.assert_called_once_with(
            event, payload)
        self.code_issue_handler.return_value.handle.assert_not_called()

    @patch("app.main.GITLAB_URL", new="test-url")
    @patch("app.main.GITLAB_TOKEN", new="test-token")
    @patch("app.main.KICAD_CODE_PROJECT_PATH", new="test-path")
    def test_gitlab(self):
        payload = {
            "a": "b",
            "project": {
                "path_with_namespace": "test-path"
            }
        }
        handler_task("Issue Hook", payload)
        self.gitlab.assert_called_once_with(
            "test-url", private_token="test-token", retry_transient_errors=True)
        self.banned_user_handler.assert_called_once_with(
            self.gitlab.return_value)
        self.code_issue_handler.assert_called_once_with(
            self.gitlab.return_value)
